#ifndef PJSIPWRAPPER_H
#define PJSIPWRAPPER_H

#include <pjsua-lib/pjsua.h>
#include <pjsua-lib/pjsua_internal.h>
#include <string>


class PjApiLogConfig;
class PjApiUa;
class PjApiUaAccount;
class PjApiUaAccountCallback;
class PjApiUaAccountHandler;

class PjApiUaConfig;
class PjApiCallInfo;


class PjApiUaAccount
{
public:

    PjApiUaAccount(std::string username,
                    std::string password,
                    std::string domain,
                    std::string registar="",
                    std::string proxy="");

    virtual ~PjApiUaAccount(){}

    int getAccountID() const
    {
        return m_id;
    }

    std::string getUsername() const
    {
        return m_username;
    }

    std::string getPassword() const
    {
        return m_password;
    }

    std::string getDisplay() const
    {
        return m_display;
    }

    std::string getDomain() const
    {
        return m_domain;
    }

    std::string getRegistar()const
    {
        return m_registar;
    }

    std::string getProxy() const
    {
        return m_proxy;
    }

    void setDisplayName(std::string name)
    {
        m_display = name;
    }

    void setDomain(std::string domain)
    {
        m_domain = domain;
    }

    void setPassword(std::string pwd)
    {
        m_password = pwd;
    }

    void setUsername(std::string name)
    {
        m_username = name;
    }

    void setRegistar(std::string server)
    {
        m_registar = server;
    }

    void setProxy(std::string server)
    {
        m_proxy = server;
    }

    void setAccountID(int id)
    {
        m_id = id;
    }


protected:

    //virtual void copy(PjApiUaAccount &account){}

    int m_id;

    std::string m_username;

    std::string m_password;

    std::string m_domain;

    std::string m_registar;

    std::string m_proxy;

    std::string m_display;

};


class PjApiUaAccountCallback
{
public:

    PjApiUaAccountCallback(PjApiUaAccountHandler *handler);

    virtual ~PjApiUaAccountCallback();

    PjApiUaAccountHandler *getAccountHandler() const
    {
        return m_accountHandler;
    }

    //call back
    virtual void onIncomingCall(int account_id,
                                int call_id,
                                void *data);

    virtual void onInfoRequest(int session_id,
                                std::string request_name,
                                std::string content_type,
                                unsigned long long content_length,
                                void *data);

    virtual void onInfoResponse(int session_id,
                                std::string response_name,
                                std::string content_type,
                                unsigned long long content_length,
                                void *data);

    virtual void onIMMessage(int call_id,
                            const std::string from,
                            const std::string to,
                            const std::string contact,
                            const std::string mime_type,
                            const std::string body);

    virtual void onIMTyping(int call_id,
                            std::string from,
                            std::string to,
                            std::string contact,
                            bool isTyping);

    virtual void onStatlessRequestReceived(std::string from,
                                                    std::string request_name,
                                                    std::string content_type,
                                                    unsigned long long content_length,
                                                    void *data);

private:
    PjApiUaAccountHandler *m_accountHandler;
};

class PjApiInviteState
{
public:
    enum InviteState{
        Unknown, 	//Before INVITE is sent or received
        Calling, 	//After INVITE is sent
        Incoming, 	//After INVITE is received.
        Early, 	    //After response with To tag.
        Connecting, 	//After 2xx is sent/received.
        Confirmed, 	//After ACK is sent/received.
        Disconnected 	//Session is terminated.
    };
};

class PjApiCallInfo
{
public:

    enum Role {
        Unknown,
        Caller,
        Callee
    };

    PjApiCallInfo(){}

    ~PjApiCallInfo(){}

    int getCallID()const
    {
        return m_callID;
    }

    void setCallID(int id);

    enum Role getRole()const
    {
        return m_role;
    }

    void setRole(enum Role role);

    int getAccountID()const
    {
        return m_accountID;
    }

    void setAccountID(int id);

    std::string getLocalUri()const
    {
        return m_localUri;
    }

    void setLocalUri(const std::string uri);

    std::string getLocalContact() const
    {
        return m_localContact;
    }

    void setLocalContact(const std::string contact);

    std::string getRemoteUri()const
    {
        return m_remoteUri;
    }

    void setRemoteUri(const std::string uri);

    std::string getRemoteContact()const
    {
        return m_remoteContact;
    }

    void setRemoteContact(const std::string contact);

    std::string getCallIDString()const
    {
        return m_callIDString;
    }

    void setCallIDString(const std::string id_string);

    enum PjApiInviteState::InviteState getCallState() const
    {
        return m_state;
    }

    void setInviteState(enum PjApiInviteState::InviteState state);

    std::string getCallStateText()const
    {
        return m_stateText;
    }

    void setCallStateText(const std::string text);

protected:

    int m_callID;

    enum Role m_role;

    int m_accountID;

    std::string m_localUri;

    std::string m_localContact;

    std::string m_remoteUri;

    std::string m_remoteContact;

    //Dialog Call-ID string.
    std::string m_callIDString;

    enum PjApiInviteState::InviteState m_state;

    std::string m_stateText;

    //To add


};

class PjApiUaAccountHandler
{
public:
    PjApiUaAccountHandler();

    ~PjApiUaAccountHandler();


    PjApiUaAccount* getAccount() const
    {
        return m_account;
    }

    PjApiUaAccountCallback *getAccountCallback() const
    {
        return m_accountCallback;
    }

    int getTransportID() const
    {
        return m_transportID;
    }

    void notifyServerRegistry(bool flag);

    void setAccountCallback(PjApiUaAccountCallback *callback);

    void setAccount(PjApiUaAccount *account);

    void setTransportID(int id);

    // To be refactored, implement transport config class
    // Here use the default configuration
    void setTransportConfig();

    void setRtpPort(int port);

    void setNullDevice();

    bool verifyUrl(const std::string url);

    // call section
    //need to be refactored since the pjsua_msg_data struct
    //Here we use the pjsua_msg_data

    int makeCall2( const std::string dst_uri,
                    void *user_data=0,
                    const pjsua_msg_data *msg_data=0);

    int makeCall( const std::string dst_uri,
                    void *user_data=0,
                    const std::string content_type="",
                    const std::string content="");

    int acceptCall(int call_id);

    int hangupCall2(int call_id,
                    int code,
                    const std::string reason="",
                    const pjsua_msg_data *msg_data=0);

    int hangupCall(int call_id,
                    int code,
                    const std::string reason="",
                    const std::string content_type="",
                    const std::string content="");

    int answerCall2(int call_id,
                    int code,
                    const std::string reason="",
                    const pjsua_msg_data *msg_data=0);

    int answerCall(int call_id,
                    int code,
                    const std::string reason="",
                    const std::string content_type="",
                    const std::string content="");

    int holdCall(int call_id, const std::string content_type="", const std::string content="");

    int resumeCall(int call_id, const std::string content_type="", const std::string content="");

    bool isCallActive(int call_id);

    bool isCallHasMedia(int call_id);

    void configCallInfoByID(int call_id, PjApiCallInfo **call_info);

    //void sendIM();
    //IM Section

    int sendIM(const std::string to_uri, const std::string mime_type, const std::string content);

    //Todo send IM with message body
    //void sendIM2(std::string to_uri, std::string mime_type, std::string content, message, data);

    int sendIMTyping(const std::string to_uri, bool is_typing);

    int sendIMInCall(int call_id, const std::string mime_type, const std::string content);

    int sendIMTypingInCall(int call_id, bool is_typing);

    int sendPackage(int call_id, const std::string name, const std::string content_type, const void * content);

    int sendPackageStateless(const std::string target_uri, const std::string from, const std::string to, const std::string contact,
                                const std::string pkg_name, const std::string content_type, const void* content);

private:

    void configAccount();

    PjApiUaAccount *m_account;

    PjApiUaAccountCallback *m_accountCallback;

    int m_transportID;

};


class PjApiUa
{
public:

    PjApiUa();

    ~PjApiUa();

    static PjApiUa *getInstance();

    void init();

    void start();

    void terminate();

    void registerAccountHandler(PjApiUaAccountHandler *handler);

    PjApiUaAccount *getUaAccount() const;

    PjApiUaAccountHandler *getUaAccountHandler() const;

    // To be refactored
    void setUaConfig(pjsua_config *config);

    void setLogConfig(pjsua_logging_config *config);

    void setMediaConfig(pjsua_media_config *config);
    //

    void setNullDevice();

    void setAccountConfig(pj_str_t id,
                        pj_str_t reg,
                        pj_str_t proxy,
                        pj_str_t username,
                        pj_str_t password,
                        pj_str_t realm);

    void setTransportConfig(pjsip_transport_type_e type, int port);

    void setMediaTransportConfig(int port);

    void setAutoAnswer(bool auto_answer);

private:



    void setCallback();

    //call back
    static void onIncomingCall(pjsua_acc_id acc_id, pjsua_call_id call_id,
			     pjsip_rx_data *rdata);

    static void onCallState(pjsua_call_id call_id, pjsip_event *e);

    static void onCallTsxState(pjsua_call_id call_id, pjsip_transaction *tsx, pjsip_event *e);

    static void onPager(pjsua_call_id call_id,
                    const pj_str_t *from,
                    const pj_str_t *to,
                    const pj_str_t *contact,
                    const pj_str_t *mime_type,
                    const pj_str_t *body);

    static void onTyping(pjsua_call_id call_id, const pj_str_t *from, const pj_str_t *to, const pj_str_t *contact, pj_bool_t is_typing);

    static pj_bool_t onStatelessRequest(pjsip_rx_data *rdata);

    static pj_status_t mod_handler_load(pjsip_endpoint *endpt);

    pjsua_config         m_uaConfig;

    pjsua_logging_config m_logConfig;

    pjsua_media_config   m_mediaConfig;

    pjsua_acc_config m_accountConfig;

    pjsua_transport_config m_transportConfig;

    pjsua_transport_config m_mediaTransportConfig;

    PjApiUaAccountHandler *m_accountHandler;

    bool m_autoAnswer;

    static PjApiUa *m_ua;

    static pjsip_module mod_default_handler;

};

/**
 * Loging config class
 */


class PjApiLogConfig
{
public:

    PjApiLogConfig();

    ~PjApiLogConfig(){}

    bool isMsgLoging() const {
        return m_msgLoging;
    }

    unsigned int getLogLevel() const {
        return m_level;
    }

    unsigned int getConsoleLogLevel() const {
        return m_consoleLevel;
    }

    /**
     * Log decoration.
     */
    unsigned int getLogDecoration() const {
        return m_decor;
    }

    std::string getLogFileName() const {
        return m_logFileName;
    }

    void setMsgLoging(bool flag) {
        m_msgLoging = flag;
    }

    void setLogLevel(unsigned int level) {
        m_level = level;
    }

    void setLogConsoleLevel(unsigned int level) {
        m_consoleLevel = level;
    }

    void setLogingDecoration(unsigned int decor) {
        m_decor = decor;
    }

    void setLogFileName(std::string name) {
        m_logFileName = name;
    }



private:

    bool m_msgLoging;

    unsigned int m_level;

    unsigned int m_consoleLevel;

    unsigned int m_decor;

    std::string m_logFileName;

};


class PjApiBuddy
{
public:
    PjApiBuddy(std::string uri, std::string name);

    ~PjApiBuddy();

    void setBuddyID(int id);

    int getBuddyID() const;

    void setBuddyURI(std::string uri);

    std::string getBuddyURI() const;

    void setBuddyDescription(std::string desc);

    std::string getBuddyDescription() const;

    void setBuddyName(std::string name);

    std::string getBuddyName() const;

    void setBuddyNickname(std::string name);

    std::string getBuddyNickname() const;


private:

    int m_id;

    std::string m_uri;

    std::string m_name;

    std::string m_description;

    std::string m_nickname;
};

class PjApiBuddyManager
{
public:

    enum BuddyStatus {
        Unknown,
        Online,
        Offline
    };

    PjApiBuddyManager();

    ~PjApiBuddyManager();

    // return the buddy id
    int addBuddy(std::string uri, bool subscribe, std::string desc="");

    void deleteBuddy(std::string uri);

    bool isValidBuddyID(int id);

    void updateBuddyPresence(std::string uri);

    int getBuddyNumber();

    std::string getBuddyUriByID(int buddy_id);

    int getBuddyIDByUri(std::string uri);

    std::string getDescriptionByUri(std::string uri);

    std::string getBuddyStatusTextByUri(std::string uri);

    std::string getBuddyStatusTextByID(int buddy_id);

    enum BuddyStatus getBuddyStatusByUri(std::string uri);

    enum BuddyStatus getBuddyStatusByID(int buddy_id);

private:


};

/*
class PjApiUaConfig
{
public:

    static PjApiUaConfig& getInstance()
    {
        static PjApiUaConfig us_config;
        return us_config;
    }

    void init();

    void setStunDomain(std::string stun) {
        m_stunDomain = stun;
    }

    void setStunHostname(std::string name) {
        m_stunHostname = name;
    }

    static void callStateChanged(pjsua_call_id call_id, pisip_event *e);

    static void onIncomingCall(pjsua_acc_id acc_id, pjsua_call_id call_id, pjsip_rx_data *rdata);

private:

    std::string m_stunDomain;

    std::string m_stunHostname;

};

*/


#endif
